/*
 * File     : MaskBitFieldHelper.java
 * Created  : 23 Jul 2011
 *
 * Copyright © 2011 Matthew Wilson (mj. {my-surname} .uk {at} gmail.com)
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
package com.googlecode.dni.type.cenum;

import com.googlecode.dni.DirectNativeInterface;

/**
 * <p>
 *  Provides helper methods for dealing with a bit field segment containing a
 *  mask.
 * </p>
 * <p>
 *  The Java enum will be treated as though it were passed into a library
 *  method; that is, the values should be annotated with {@link CEnumValue} as
 *  appropriate.  The values must be the bit mask values and <em>not</em> the
 *  it numbers; for example, values of 1, 2, 4, 8, ... (not 0, 1, 2, 3, ...).
 *  If the segment does not start at bit zero, the values are optionally shifted
 *  left; for example, an enum value of 4 for a segment starting at bit 10 could
 *  become <code>4&lt;&lt;10</code> in the bit field; see the documentation on
 *  the factory method for more information.
 * </p>
 * <h3>Example</h3>
 * <p>C code:
 * <pre>
 *  typedef enum flags
 *  {
 *    NONE = 0,
 *
 *    // some boolean flags
 *    FLAG_1 = 0x1,
 *    FLAG_2 = 0x2,
 *    FLAG_3 = 0x4,
 *
 *    // some mutually exclusive values
 *    FRUIT_TYPE_MASK = 0xf0,
 *    UNKNOWN_FRUIT   = 0x00,
 *    BANANA          = 0x10,
 *    APPLE           = 0x20,
 *    ORANGE          = 0x30,
 *
 *    // some more mutually exclusive values
 *    SIZE_TYPE_MASK  = 0x300,
 *    UNDEFINED_SIZE  = 0x000,
 *    SMALL           = 0x100,
 *    MEDIUM          = 0x200,
 *    LARGE           = 0x300,
 *  } flags_t;
 *
 *  flags_t smallApple = FLAG_1;
 *  smallApple |= APPLE;
 *  smallApple |= SMALL;
 *
 *  flags_t mediumApple = ( smallApple & ~SIZE_TYPE_MASK ) | MEDIUM;
 * </pre>
 * <p>Equivalent type-safe Java code:</p>
 * <pre>
 *  enum CoreFlags implements MaskBitField&lt; CoreFlags &gt;
 *  {
 *      &#64;CEnumValue( 1 ) FLAG_1,
 *      &#64;CEnumValue( 2 ) FLAG_2,
 *      &#64;CEnumValue( 4 ) FLAG_3;
 *
 *      public static final MaskBitFieldHelper&lt; CoreFlags &gt; HELPER =
 *          DirectNativeInterface.getMaskBitFieldHelper( 1 | 2 | 4, 0, CoreFlags.class );
 *
 *      public int withValue( int field, boolean value ) { return HELPER.withValue( field, this, value ); }
 *      public int set( int field ) { return HELPER.withSet( field, this ); }
 *      public int clear( int field ) { return HELPER.withClear( field, this ); }
 *      public boolean isSet( int field ) { return HELPER.isSet( field, this ); }
 *  }
 *  enum Fruit implements EnumBitField&lt; Fruit &gt;
 *  {
 *      &#64;CEnumValue( 0 ) UNKNOWN,
 *      &#64;CEnumValue( 1 ) BANANA,
 *      &#64;CEnumValue( 2 ) APPLE,
 *      &#64;CEnumValue( 3 ) ORANGE;
 *
 *      public static final EnumBitFieldHelper&lt; Fruit &gt; HELPER =
 *          DirectNativeInterface.getEnumBitFieldHelper( 0xf, 4, Fruit.class );
 *
 *      public int set( int field, Fruit value ) { return HELPER.with( field, this, value ); }
 *      public boolean is( int field ) { return HELPER.get( field ) == this; }
 *  }
 *  enum Size
 *  {
 *      &#64;CEnumValue( 0 ) UNDEFINED,
 *      &#64;CEnumValue( 1 ) SMALL,
 *      &#64;CEnumValue( 2 ) MEDIUM,
 *      &#64;CEnumValue( 3 ) LARGE;
 *
 *      public static final EnumBitFieldHelper&lt; Size &gt; HELPER =
 *          DirectNativeInterface.getEnumBitFieldHelper( 0xf, 8, Fruit.class );
 *
 *      public int set( int field, Fruit value ) { return HELPER.with( field, this, value ); }
 *      public boolean is( int field ) { return HELPER.get( field ) == this; }
 *  }
 *
 *  int smallApple = CoreFlags.FLAG_1.set( 0 );
 *  smallApple = Fruit.APPLE.set( smallApple );
 *  smallApple = Size.SMALL.set( smallApple );
 *
 *  int mediumApple = Size.MEDIUM.set( smallApple );
 *
 *  // alternative
 *  int smallApple = CoreFlags.HELPER.withSet( 0, CoreFlags.FLAG_1 );
 *  smallApple = Fruit.HELPER.with( smallApple, Fruit.APPLE );
 *  smallApple = Size.HELPER.with( smallApple, Size.SMALL );
 *
 *  int mediumApple = Size.HELPER.with( smallApple, MEDIUM );
 * </pre>
 *
 * @param <E>
 *           the enum type
 *
 * @see DirectNativeInterface#getMaskBitFieldHelper(Class,int)
 *
 * @author Matthew Wilson
 */
public interface MaskBitFieldHelper< E extends Enum< E > >
{

    /**
     * Takes the given field value and sets or clears the given enum value.
     *
     * @param field
     *           the existing field value
     * @param enumValue
     *           the enum value to set or clear
     * @param set
     *           <code>true</code> to set the value;
     *           <code>false</code> to clear it
     * @return the updated field value
     */
    int withValue( int field, E enumValue, boolean set );

    /**
     * Takes the given field value and sets the given enum value.
     *
     * @param field
     *           the existing field value
     * @param enumValue
     *           the enum value to set
     * @return the updated field value
     */
    int withSet( int field, E enumValue );

    /**
     * Takes the given field value and clears the given enum value.
     *
     * @param field
     *           the existing field value
     * @param enumValue
     *           the enum value to clear
     * @return the updated field value
     */
    int withClear( int field, E enumValue );

    /**
     * Determines whether the given enum value is set in the given field value.
     *
     * @param field
     *            the field value
     * @param enumValue
     *            the enum value to test
     * @return <code>true</code> iff the enum value is set
     */
    boolean isSet( int field, E enumValue );

    /**
     * <p>
     *  Forms a string for debugging purposes that indicates which of the enum
     *  values used by this helper are set in the given field.
     * </p>
     * <p>
     *  The string format is implementation-, platform-, and
     *  version-dependent.
     * </p>
     *
     * @param field
     *            the field value
     * @return a string representation
     */
    String toString( int field );


    /**
     * <p>
     *  Helper interface for enum types that are to be used with
     *  {@link MaskBitFieldHelper}.
     * </p>
     * <p>
     *  The methods can delegate to a static {@link MaskBitFieldHelper} instance
     *  as given in the {@linkplain MaskBitFieldHelper example}.
     * </p>
     *
     * @param <E>
     *           the enum type
     *
     * @author Matthew Wilson
     */
    public interface MaskBitField< E extends Enum< E > & MaskBitField< E > >
    {
        /**
         * <p>
         *  Takes the given field value and sets or clears this enum value.
         * </p>
         * <p>
         *  Implement this as:
         * </p>
         * <pre>
         *  return HELPER.withValue( field, this, set );
         * </pre>
         *
         * @param field
         *           the existing field value
         * @param set
         *           <code>true</code> to set the value;
         *           <code>false</code> to clear it
         * @return the updated field value
         */
        int withValue( int field, boolean set );

        /**
         * <p>
         *  Takes the given field value and sets this enum value.
         * </p>
         * <p>
         *  Implement this as:
         * </p>
         * <pre>
         *  return HELPER.withSet( field, this );
         * </pre>
         *
         * @param field
         *           the existing field value
         * @return the updated field value
         */
        int set( int field );

        /**
         * <p>
         *  Takes the given field value and clears this enum value.
         * </p>
         * <p>
         *  Implement this as:
         * </p>
         * <pre>
         *  return HELPER.withClear( field, this );
         * </pre>
         *
         * @param field
         *           the existing field value
         * @return the updated field value
         */
        int clear( int field );

        /**
         * <p>
         *  Determines whether this enum value is set in the given field value.
         * </p>
         * <p>
         *  Implement this as:
         * </p>
         * <pre>
         *  return HELPER.isSet( field, this );
         * </pre>
         *
         * @param field
         *            the field value
         * @return <code>true</code> iff the enum value is set
         */
        boolean isSet( int field );
    }

}
